//MADE IN RUSSIA

#include <malloc.h>
#include "../../Libs/TXLib.h" // "../../Libs/TXLib.h"

#define FUNC_S //cout << __PRETTY_FUNCTION__ << " started.\n";
#define FUNC_F //cout << __PRETTY_FUNCTION__ << " finished.\n";

#include "VectorExceptions.h"

using std::cout;
using std::endl;
using std::string;

#define ADD_EXC_DATA __PRETTY_FUNCTION__, __LINE__

template <class Type>
class VectorAllocator
{
    protected:
        Type* data_;
        int* owner_count_;
        int  size_;

    public:
        VectorAllocator();
        VectorAllocator(int size);
        VectorAllocator(const VectorAllocator& v_ref);
        const VectorAllocator<Type>& operator = (const VectorAllocator& v_ref);

        void Increase();

        ~VectorAllocator();

};

template <class Type>
class Vector : public VectorAllocator<Type>
{
    public:
        Vector();
        Vector(int new_size);
        //Vector(const Vector& v_ref);

        int size();

        Type at(int n);
        void at(int n, Type new_val);
        void push_back(Type element);
        void pop_back();
        void insert(int position, Type val);
        void erase(int position);
        void clear();
        void assign(Vector<Type> new_vec, int pos1, int pos2);
        void assign(int new_size, Type val);
        void swap(int pos1, int pos2);
        int  find(Type element);
        void resize(int new_size);

        Type&        operator [] (int index);
        //const Vector<Type>& operator = (const Vector& v_ref);

        inline bool ok() const;
        void dump() const;
};

template<class Type>
VectorAllocator<Type>::VectorAllocator():

    data_(new Type[0]),
    size_(0),
    owner_count_(new int(1))

    {
    FUNC_S
    /*assert (this->ok())*/;
    FUNC_F
    }

template<class Type>
VectorAllocator<Type>::VectorAllocator(int size):

    data_(new Type[size]),
    size_(size),
    owner_count_(new int(1))

    {
    FUNC_S

    if(size < 0) throw SizeFaultException(ADD_EXC_DATA, size);

    FUNC_F
    }

template<class Type>
VectorAllocator<Type>::~VectorAllocator()
{
    FUNC_S
    //cout << "Destructor called\n";

    if(*owner_count_ <= 1)
    {
        //cout << "Data destructed\n";

        delete[] data_;      data_ = NULL;
        delete owner_count_; owner_count_ = NULL;
        //printf ("U!");
        ///*$$$$*/
    }
    else
    {
        assert(this);
        assert(this->owner_count_);

        (*this->owner_count_)--;
    }
    FUNC_F
}

template<class Type>
void VectorAllocator<Type>::Increase()
{
    FUNC_S
    ///*assert (this->ok())*/;

    (*owner_count_)++;

    ///*assert (this->ok())*/;
    FUNC_F
}

template<class Type>
void Vector<Type>::resize(int new_size)
{
    FUNC_S

    if(new_size < 0) throw SizeFaultException(ADD_EXC_DATA, new_size);
    //cout << "Resize is called\n";
    /*assert (this->ok())*/;

    /*$$$$*/ assert(ok());

    /*$$$$*/ int prev_size = size();

    /*$$$$*/ Type* temp = new Type[new_size];

    /*$$$$*/ assert (this);
    /*$$$$*/ std::copy(this->data_, this->data_ + std::min(new_size, prev_size), temp);

     /*$$$$*/ std::swap(temp, this->data_);
    delete[] temp;

    /*$$$$*/ assert(this);

    /*$$$$*/ this->size_ = new_size;

    /*assert (this->ok())*/;
    //cout << "Resize is ended\n";
    FUNC_F
}

template<class Type>
Vector<Type>::Vector():

    VectorAllocator<Type>()

    {
    FUNC_S
    //cout << "Default constructor called.\n";
    /*assert (this->ok())*/;
    FUNC_F
    }

template<class Type>
Vector<Type>::Vector(int new_size):

    VectorAllocator<Type>(new_size)

    {
    FUNC_S
    assert(this);
    assert (ok());

    //cout << "Now I'm usual print. And I'm in constructor of Vector! I'm going to call clear()\n";

    clear();
    /*assert (this->ok())*/;
    FUNC_F
    }

template<class Type>
VectorAllocator<Type>::VectorAllocator(const VectorAllocator& v_ref):

    data_(v_ref.data_),
    size_(v_ref.size_),
    owner_count_(v_ref.owner_count_)

    {
        FUNC_S
        ///*assert (this->ok())*/;

        Increase();

        ///*assert (this->ok())*/;
        FUNC_F
    }

template<class Type>
const VectorAllocator<Type>& VectorAllocator<Type>::operator = (const VectorAllocator<Type>& v_ref)
{
    FUNC_S
    /*assert (this->ok())*/;

    data_ = v_ref.data_;
    size_ = v_ref.size_;
    owner_count_ = v_ref.owner_count_;

    Increase();

    /*assert (this->ok())*/;
    FUNC_F
    return v_ref;
}

template<class Type>
int Vector<Type>::size()
{
    FUNC_S
    /*$$$$*/
    ///*assert (this->ok())*/;  // Some strange thing...
    assert(this);
    FUNC_F
    return this->size_;
}

template<class Type>
Type Vector<Type>::at(int n)
{
    FUNC_S
    /*assert (this->ok())*/;

    if(n >= (size() - 1) || n < 0) throw IndexFaultException(ADD_EXC_DATA, n, size());

    //assert(n < size());
    //assert(n >= 0);
    assert(this);

    FUNC_F
    return this->data_[n];
}

template<class Type>
void Vector<Type>::at(int n, Type new_val)
{
    FUNC_S
    /*assert (this->ok())*/;

    if(n < 0 || n >= size()) throw SizeFaultException(ADD_EXC_DATA, n, size());

    assert(this);

    this->data_[n] = new_val;
    FUNC_F
}

template<class Type>
inline bool Vector<Type>::ok() const
{
    FUNC_S FUNC_F
    //assert (_heapchk() == 0);

    return (size() >= 0);
}

template<class Type>
void Vector<Type>::dump() const
{
    FUNC_S
    cout << "It's dump of Vector::dump()\n"
         << "{\n"
         << "    size() = " << size() << "\n\n";

    //assert(0 >= 0); assert(0 < size());
    assert(this);

    if(size() > 0) cout << "    data[0] = " << this->data_[0] << "\n";

    for(int i = 1; i < size(); i++)
    {
        assert(i >= 0); assert(i < size());
        assert(this);

        cout << "        " << "[" << i << "]" << " = " << this->data_[i] << ";\n";
    }

    cout << "Status is ";

    string str = "";

    //cout << "Size is " << size() << std::endl;


    if(ok())
    {
        //cout << "OK returned true! I want to type \"OK\"\n";

        $g str = "OK";

        //cout << "Str equaled\n";
    }
    else
    {
        //cout << "OK returned false! I want to type false message!\n";

        $r str = "\"You have done something that makes me cry :(\"";

        //cout << "Str equaled\n";
    }

    cout << str << "\n"; $d

    cout << "}\n";
    FUNC_F
}

template<class Type>
void Vector<Type>::push_back(Type new_elem)
{
    FUNC_S
    /*assert (this->ok())*/;

    if(size() + 1 < 0) throw SizeFaultException(ADD_EXC_DATA, size() + 1);

    resize(size() + 1);

    int pos = size() - 1;

    if(pos < 0 || pos >= size()) throw IndexFaultException(ADD_EXC_DATA, pos, size());

    //assert(pos >= 0); assert(pos < size());
    assert(this);

    this->data_[pos] = new_elem;

    /*assert (this->ok())*/;
    FUNC_F
}

template<class Type>
void Vector<Type>::pop_back()
{
    FUNC_S
    /*assert (this->ok())*/;

    resize(size() - 1);

    /*assert (this->ok())*/;
    FUNC_F
}

template<class Type>
Type& Vector<Type>::operator[] (int index)
{
    FUNC_S
    /*assert (this->ok())*/;

    //assert(index >= 0); assert(index < size());
    if(index < 0 || index >= size()) throw IndexFaultException(ADD_EXC_DATA, index, size());

    assert(this);

    Type& ref_result = this->data_[index];

    /*assert (this->ok())*/;

    FUNC_F
    return ref_result;
}

template<class Type>
void Vector<Type>::insert(int position, Type val)
{
    FUNC_S
    resize(size() + 1);

    for(int i = size() - 2; i >= position; i--)
    {
        //assert(i     < size()); assert(i     >= 0);
        //assert(i + 1 < size()); assert(i + 1 >= 0);

        if(i     >= size() || i  < 0)    throw IndexFaultException(ADD_EXC_DATA, i,     size());
        if(i + 1 >= size() || i + 1 < 0) throw IndexFaultException(ADD_EXC_DATA, i + 1, size());

        assert(this);
        this->data_[i + 1] = this->data_[i];
    }

    //assert(position >= 0); assert(position < size());

    if(position >= size() || position < 0) throw IndexFaultException(ADD_EXC_DATA, position, size());

    this->data_[position] = val;
    FUNC_F
}

template<class Type>
void Vector<Type>::erase(int position)
{
    FUNC_S
    for(int i = position; i < size() - 2; i++)
    {
        //assert(i     < size()); assert(i     >= 0);
        //assert(i + 1 < size()); assert(i + 1 >= 0);
        assert(this);

        if(i     >= size() || i  < 0)    throw IndexFaultException(ADD_EXC_DATA, i,     size());
        if(i + 1 >= size() || i + 1 < 0) throw IndexFaultException(ADD_EXC_DATA, i + 1, size());

        this->data_[i] = this->data_[i + 1];
    }

    pop_back();
    FUNC_F
}

template<class Type>
void Vector<Type>::clear()
{
    //cout << "And now I'm in clear()\n";

    assert(this);
    /*assert(this->ok())*/;
    FUNC_S
    for(int i = 0; i < size(); i++)
    {
        //assert(i >= 0); assert(i < size());
        assert(this);

        if(i < 0 || i >= size()) throw IndexFaultException(ADD_EXC_DATA, i, size());

        this->data_[i] = ZERO(Type);
    }
    assert(this);
    /*assert(this->ok())*/;
    FUNC_F
}

template<class Type>
void Vector<Type>::assign(Vector<Type> new_vec, int pos1, int pos2)
{
    FUNC_S
    resize(new_vec.size());

    for(int i = 0; i < pos2 - pos1; i++)
    {
        //assert(i >= 0); assert(i < size());

        if(i < 0 || i >= size()) throw IndexFaultException(ADD_EXC_DATA, i, size());

        assert(this);

        this->data_[i] = new_vec[pos1 + i];
    }
    FUNC_F
}

template<class Type>
void Vector<Type>::assign(int new_size, Type num)
{
    FUNC_S
    resize(new_size);

    for(int i = 0; i < new_size; i++)
    {
        if(i < 0 || i >= size()) throw IndexFaultException(ADD_EXC_DATA, i, size());
        assert(this);

        this->data_[i] = num;
    }
    FUNC_F
}

template<class Type>
void Vector<Type>::swap(int pos1, int pos2)
{
    FUNC_S
    if(pos1 < 0 || pos1 >= size()) throw IndexFaultException(ADD_EXC_DATA, pos1, size());
    if(pos2 < 0 || pos2 >= size()) throw IndexFaultException(ADD_EXC_DATA, pos2, size());
    assert(this);

    int temp = this->data_[pos1];

    this->data_[pos1] = this->data_[pos2];
    this->data_[pos2] = temp;
    FUNC_F
}

template<class Type>
int Vector<Type>::find(Type element)
{
    FUNC_S
    for(int i = 0; i < size(); i++)
    {
        if(i < 0 || i >= size()) throw IndexFaultException(ADD_EXC_DATA, i, size());
        assert(this);

        if(this->data_[i] == element) return i;
    }

    FUNC_F

    return -1;
}
